package diycode

import (
	"fmt"
	"strconv"
	"strings"

	"gitee.com/minph/siphan/exception"
)

const CodeStr = `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,.?!:;+-*/^@0123456789%<>#$&=_|()\"'{}[]~` + "` "

// 单个字符项
type Code struct {
	Char  string
	Index int
}

// 字符项数组
type CodeSlice []Code

// 字符项数组查找指定字符
// return nil if not found
func (c CodeSlice) FindChar(char string) (*Code, error) {
	for i, v := range c {
		if v.Char == char {
			return &c[i], nil
		}
	}

	return nil, exception.ErrCharNotFound{
		Char: char,
	}
}

// 字符项数组查找指定index位置
// return nil if not found
func (c CodeSlice) FindIndex(index int) (*Code, error) {
	for _, v := range c {
		if v.Index == index {
			return &c[index], nil
		}
	}

	return nil, exception.ErrIndexNotFound{
		Index: index,
	}
}

func (c CodeSlice) GenStr() string {
	var res string
	for _, s := range c {
		res += s.Char
	}
	return res
}

// 生成默认字符项数组
func GetCodeSlice() CodeSlice {
	return GetCodeSliceFromStr(CodeStr)
}

// 生成随即乱序字符项数组
func GetRandCodeSlice() (CodeSlice, string) {
	randStr := ShuffleStr(CodeStr)
	return GetCodeSliceFromStr(randStr), randStr
}

// 从自定义字符串生成字符项数组
func GetCodeSliceFromStr(str string) CodeSlice {
	res := make([]Code, strLen(str))
	splitStr := strings.Split(str, "")

	for index, char := range splitStr {
		res[index] = Code{Char: char, Index: index}
	}

	return res
}

// 通过 CodeSlice 将指定 string 转为 Hex 码
func (c CodeSlice) GenInputHexData(text string) (*HexKey, error) {
	res := HexKey{
		List:       []string{},
		OrderSlice: c,
	}
	for _, item := range strings.Split(text, "") {
		target, err := c.FindChar(item)
		if err != nil {
			return nil, err
		}
		key := fmt.Sprintf("%02X", target.Index)
		res.List = append(res.List, key)

	}

	return &res, nil
}

func (c CodeSlice) ParseHexData(h *HexKey) (string, error) {
	return c.ParseHexDataList(h.List)
}

func (c CodeSlice) ParseHexDataList(list []string) (string, error) {
	var res string
	for _, item := range list {
		num, err := strconv.ParseInt(item, 16, 32)
		if err != nil {
			return "", err
		}

		target, err := c.FindIndex(int(num))
		if err != nil {
			return "", err
		}

		res += target.Char
	}

	return res, nil
}
